﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Numerics;

namespace CractalLibrary
{

    public partial class cractal
    {

        private char[] valueset;

        public char[] value;
        private char[] StandardIncrementValue;
        private char minvalue;
        private char maxvalue;
        private char valueof1;
        private int maxcodesize;
        private int mincodesize;
        public delegate void IncrementedHandler(object sender, IncrementEventArgs e);
        public event IncrementedHandler OnIncremented;

        public object Clock;
        
        
        private List<string> Paddingvalues;

        public cractal(char[] vset)
        {
            Initilisation(ref vset);
            

            StandardIncrementValue = new char[] { this.valueof1 };

        }

        private void Initilisation(ref char[] vset)
        {
            Clock = new object();
            valueset = new char[vset.Length];
            int x = 0;
            foreach (char item in vset)
            {
                valueset[x] = item;
                x++;
            }
            minvalue = valueset[0];
            valueof1 = valueset[1];
            maxvalue = valueset[valueset.Length - 1];
            //initial value for the cracktal
            value = new char[1];
            value[0] = valueset[0];
        }
        public cractal(char[] vset,int Mcodesize)
        {
            Initilisation(ref vset);
            maxcodesize = Mcodesize;
            StandardIncrementValue = new char[] { this.valueof1 };

        }

        private void Setupcharactureset()
        {

            List<int> ignoredchars = new List<int>();
            ignoredchars.AddRange(new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 127 });
            valueset = new char[(128 - ignoredchars.Count)];
            int y = 0;
            for (int x = 0; x < valueset.Length; x++)
            {

                while (ignoredchars.Contains(y))
                {
                    y++;
                }
                valueset[x] = (Char)(y);

                y++;


            }
            minvalue = valueset[0];
            maxvalue = valueset[valueset.Length - 1];
            //initial value for the cracktal
            value = new char[1];
            value[0] = valueset[0];
            valueof1 = valueset[1];
            StandardIncrementValue = new char[] { this.valueof1 };
        }
        public cractal()
        {
            Clock = new object();
            Setupcharactureset();

        }

        public cractal(char[] vset, int MaxCodeSize, ref List<string> paddingvalues,int MinCodeSize)
        {
            // TODO: Complete member initialization
            Initilisation(ref vset);

            Paddingvalues = paddingvalues;
            mincodesize = MinCodeSize;
            maxcodesize = MaxCodeSize;
            StandardIncrementValue = new char[] { this.valueof1 };
        }

        public void increment()
        {
            lock (Clock)
            {
                
               
                value = (ADD(this.value, StandardIncrementValue)).ToArray();
                if (value.Length >= mincodesize)
                {
                    FireIncrementedEvent();
                }
                
                if (value.Length < maxcodesize)
                {
                    
                        int neededpadding = maxcodesize - value.Length;

                        while (neededpadding > 0)
                        {
                            string currentval = new string(this.value);
                            currentval = Paddingvalues.Where(z => z.Length == neededpadding).FirstOrDefault() + currentval;
                            if (currentval.Length >= mincodesize)
                            {
                                FireIncrementedPaddingEvent(currentval);
                            }
                            neededpadding--;
                        }
                    

                }
                
            }
        }
        private void FireIncrementedPaddingEvent(string val)
        {

            // Make sure someone is listening to event
            if (OnIncremented == null) return;

            IncrementEventArgs args = new IncrementEventArgs(val.ToCharArray());
            OnIncremented(this, args);
        }

        private void FireIncrementedEvent()
        {

            // Make sure someone is listening to event
            if (OnIncremented == null) return;

            IncrementEventArgs args = new IncrementEventArgs(this.value);
            OnIncremented(this, args);
        }


        private void addone()
        {
            int currentvaluelength = value.Length;
            bool additioncomplete = false;
            int orderofPower = 1;

            while (!additioncomplete)
            {
                if ((currentvaluelength - orderofPower) < 0)
                {

                    PerformaditionsetnewValue();
                    additioncomplete = true;
                }
                else
                {

                    if (value[value.Length - (orderofPower)] == maxvalue)
                    {


                        value[value.Length - (orderofPower)] = minvalue;

                        orderofPower++;

                    }
                    else
                    {

                        {
                            value[value.Length - (orderofPower)] = valueset[Array.IndexOf(valueset, (value[value.Length - (orderofPower)])) + 1];
                            additioncomplete = true;
                        }
                    }

                }
            }

        }

        private void addone(ref char[] number)
        {
            int currentvaluelength = number.Length;
            bool additioncomplete = false;
            int orderofPower = 1;

            while (!additioncomplete)
            {
                if ((currentvaluelength - orderofPower) < 0)
                {

                    PerformaditionsetnewValue(ref number);
                    additioncomplete = true;
                }
                else
                {

                    if (number[number.Length - (orderofPower)] == maxvalue)
                    {
                        number[number.Length - (orderofPower)] = minvalue;
                        orderofPower++;

                    }
                    else
                    {

                        {
                            number[number.Length - (orderofPower)] = valueset[Array.IndexOf(valueset, (number[number.Length - (orderofPower)])) + 1];
                            additioncomplete = true;
                        }
                    }

                }
            }

        }

        private void PerformaditionsetnewValue(ref char[] number)
        {
            char[] tempnumber;
            tempnumber = new char[number.Length + 1];
            tempnumber[0] = valueof1;
            number[0] = minvalue;
            int x = 1;
            foreach (char item in number)
            {
                tempnumber[x] = item;
                x++;
            }
            number = new char[number.Length + 1];
            number = (char[])tempnumber.Clone();
        }

        private void PerformaditionsetnewValue()
        {
            char[] tempnumber;
            tempnumber = new char[value.Length + 1];
            tempnumber[0] = valueof1;
            value[0] = minvalue;
            int x = 1;
            foreach (char item in value)
            {
                tempnumber[x] = item;
                x++;
            }
            value = new char[value.Length + 1];
            value = (char[])tempnumber.Clone();

        }


        public char MaxUnitValue
        {
            get
            {
                return maxvalue;
            }

        }

        public char MinUnitValue
        {
            get
            {
                return minvalue;
            }

        }




        public char[] RoughDivideBy(char[] endvalue, int divideby)
        {

            List<char> res = new List<char>(); //divisionresult

            string tempstorepreviousvalueatindex = "";
            int modulus = -1;
            int currentordreofpower = -1;
            for (int x = 0; x < endvalue.Length; x++) //start at most significant digit
            {
                currentordreofpower = (endvalue.Length - 1) - x;
                int valueatindex = getintvalueATindex(ref endvalue, ref x);




                if (tempstorepreviousvalueatindex == "")
                {
                    if (modulus == -1)
                    {
                        if (valueatindex < divideby)
                        {
                            tempstorepreviousvalueatindex = endvalue[x].ToString();

                        }
                        else
                        {
                            int divisionResult = (valueatindex / divideby);
                            res.Add(valueset[divisionResult]); //zero based index
                            modulus = valueatindex % divideby;
                            if (modulus == 0) //remainder
                            {
                                modulus = -1; //no remainder so set it back to default
                            } // no else here modulus stored.
                        }
                    }
                    else //remainder detected from previous iteration must be included in the calculation
                    {

                        var subdivision = (stringValueofINT(modulus) + endvalue[x].ToString()).ToCharArray();
                        int INTvalueofsubdivision = getIntValueofCractal(subdivision);
                        int INTdivisionResult = (INTvalueofsubdivision / divideby);
                        foreach (char i in stringValueofINT(INTdivisionResult))
                        {
                            res.Add(i);
                        }
                        //   res.Add(valueset[INTdivisionResult - 1]); this should work
                        modulus = (int)(INTvalueofsubdivision % divideby);
                        if (modulus == 0) //remainder
                        {
                            modulus = -1; //no remainder so set it back to default
                        } // no else here modulus stored..



                    }
                }
                else //this means that the digit obtained in the previous or X number previous iterations was less than the divided by
                {
                    if (res.Count > 0)
                    {
                        res.Add(minvalue);
                    }
                    var subdivision = (tempstorepreviousvalueatindex + endvalue[x].ToString()).ToCharArray();
                    int INTvalueofsubdivision = getIntValueofCractal(subdivision);
                    if (INTvalueofsubdivision < divideby)
                    {
                        tempstorepreviousvalueatindex = (tempstorepreviousvalueatindex) + endvalue[x].ToString();

                    }
                    else
                    {

                        int INTdivisionResult = (INTvalueofsubdivision / divideby);
                        foreach (char i in stringValueofINT(INTdivisionResult))
                        {
                            res.Add(i);
                        }
                        tempstorepreviousvalueatindex = "";
                        modulus = (INTvalueofsubdivision % divideby);
                        if (modulus == 0) //remainder
                        {
                            modulus = -1; //no remainder so set it back to default
                        } // no else here modulus stored.
                    }
                }


            }
            if (tempstorepreviousvalueatindex != "")
            {
                res.Add(this.minvalue);
            }

            return res.ToArray();


        }

        private string stringValueofBIGINT(BigInteger modulus)
        {
            
            var tempnumber = new char[1];
            tempnumber[0] = minvalue;

            for (BigInteger i = (modulus); i > 0; i--)
            {
                addone(ref tempnumber);

            }

            return new String(tempnumber);
        }

        private string stringValueofINT(int modulus)
        {
            
            var tempnumber = new char[1];
            tempnumber[0] = minvalue;
            for (int i = (modulus); i > 0; i--)
            {
                addone(ref tempnumber);

            }

            return new String(tempnumber);
        }


        private BigInteger getBIGINTValueofCractal(char[] subdivision)
        {
            BigInteger val = 0;
            var subreverse = subdivision;
            Array.Reverse(subreverse);

            int orderofPower = 0;

            foreach (char item in subreverse)
            {
                int temp = Array.IndexOf(valueset, item);
                val = val + ((BigInteger)temp * ((BigInteger)Math.Pow((double)valueset.Length, (double)orderofPower)));
                orderofPower++;
            }

            return val;
        }

        private int getIntValueofCractal(char[] subdivision)
        {
            int val = 0;
            var subreverse = subdivision;
            Array.Reverse(subreverse);

            int orderofPower = 0;

            foreach (char item in subreverse)
            {
                int temp = Array.IndexOf(valueset, item);
                val = val + (temp * ((int)Math.Pow((double)valueset.Length, (double)orderofPower)));
                orderofPower++;
            }

            return val;
        }

        private int getintvalueATindex(ref char[] subdivision, ref int index)
        {
           
            return Array.IndexOf(valueset, subdivision[index]);
            
        }

      


        public IEnumerable<char> ADD(char[] Original, char[] rADD)
        {
            Stack<char> resultofaddtion = new Stack<char>(); 
            
            
            
            //numbers start [add] at least significant digit.
            Array.Reverse(Original);
            Array.Reverse(rADD);
            char? carryover = null;

            if (rADD.Length >= Original.Length)
            {
                //iterate through the largest number for addition. / reverse
                //Lowest order of power first
                for (int x = 0; x < rADD.Length; x++)
                {
                    if (x >= Original.Length)
                    {
                        if (carryover != null)
                        {
                            int ValueofDigitToAdd = getIntValueofCractal(new char[] { rADD[x] });
                            int ValueofDigitNumberActedOn = getIntValueofCractal(new char[] { carryover.Value });
                            int digitsummation = ValueofDigitToAdd + ValueofDigitNumberActedOn;
                            if (digitsummation >= valueset.Length)
                            {
                                var cracktalstringvalueOFsummation = stringValueofINT(digitsummation);
                                if (cracktalstringvalueOFsummation.Length == 2) //and it better be
                                {
                                    resultofaddtion.Push(cracktalstringvalueOFsummation[1]);
                                    carryover = cracktalstringvalueOFsummation[0];
                                }
                            }
                            else
                            {
                                
                                resultofaddtion.Push((stringValueofINT(digitsummation))[0]);
                                carryover = null;
                            }
                        }
                        else
                        {
                            resultofaddtion.Push(rADD[x]);
                        }
                    }
                    else
                    {
                        int ValueofDigitToAdd = getIntValueofCractal(new char[] { rADD[x] });
                        int ValueofDigitNumberActedOn = getIntValueofCractal(new char[] { Original[x] });

                        if (carryover != null)
                        {
                            int ValueofCarryOver = getIntValueofCractal(new char[] { carryover.Value });
                            int digitsummation = ValueofDigitToAdd + ValueofDigitNumberActedOn + ValueofCarryOver;
                            if (digitsummation >= valueset.Length)
                            {
                                var cracktalstringvalueOFsummation = stringValueofINT(digitsummation);
                                if (cracktalstringvalueOFsummation.Length == 2) //and it better be
                                {
                                    resultofaddtion.Push(cracktalstringvalueOFsummation[1]);
                                    carryover = cracktalstringvalueOFsummation[0];
                                }
                            }
                            else
                            {
                                var cracktalstringvalueOFsummation = stringValueofINT(digitsummation);
                                resultofaddtion.Push(cracktalstringvalueOFsummation[0]);
                                carryover = null;
                            }

                        }
                        else
                        {
                            int digitsummation = ValueofDigitToAdd + ValueofDigitNumberActedOn;
                            if (digitsummation >= valueset.Length)
                            {
                                var cracktalstringvalueOFsummation = stringValueofINT(digitsummation);
                                if (cracktalstringvalueOFsummation.Length == 2) //and it better be
                                {
                                    resultofaddtion.Push(cracktalstringvalueOFsummation[1]);
                                    carryover = cracktalstringvalueOFsummation[0];
                                }
                            }
                            else
                            {
                                var cracktalstringvalueOFsummation = stringValueofINT(digitsummation);
                                resultofaddtion.Push(cracktalstringvalueOFsummation[0]);
                                carryover = null;
                            }
                        }

                    }
                }
            }
            else
            {
                //iterate through the largest number for addition. / reverse
                //Lowest order of power first
                for (int x = 0; x < Original.Length; x++)
                {
                    if (x >= rADD.Length)
                    {
                        if (carryover != null)
                        {
                            int ValueofDigitToAdd = getIntValueofCractal(new char[] { Original[x] });
                            int ValueofDigitNumberActedOn = getIntValueofCractal(new char[] { carryover.Value });
                            int digitsummation = ValueofDigitToAdd + ValueofDigitNumberActedOn;
                            if (digitsummation >= valueset.Length)
                            {
                                var cracktalstringvalueOFsummation = stringValueofINT(digitsummation);
                                if (cracktalstringvalueOFsummation.Length == 2) //and it better be because the number is greater than the valueset length
                                {
                                    resultofaddtion.Push(cracktalstringvalueOFsummation[1]);
                                    carryover = cracktalstringvalueOFsummation[0];
                                }
                            }
                            else
                            {

                                resultofaddtion.Push((stringValueofINT(digitsummation))[0]);
                                carryover = null;

                            }
                        }
                        else
                        {
                            resultofaddtion.Push(Original[x]);
                        }
                    }
                    else
                    {
                        int ValueofDigitToAdd = getIntValueofCractal(new char[] { rADD[x] });
                        int ValueofDigitNumberActedOn = getIntValueofCractal(new char[] { Original[x] });

                        if (carryover != null)
                        {
                            int ValueofCarryOver = getIntValueofCractal(new char[] { carryover.Value });
                            int digitsummation = ValueofDigitToAdd + ValueofDigitNumberActedOn + ValueofCarryOver;
                            if (digitsummation >= valueset.Length)
                            {
                                var cracktalstringvalueOFsummation = stringValueofINT(digitsummation);
                                if (cracktalstringvalueOFsummation.Length == 2) //and it better be
                                {
                                    resultofaddtion.Push(cracktalstringvalueOFsummation[1]);
                                    carryover = cracktalstringvalueOFsummation[0];
                                }
                            }
                            else
                            {

                                resultofaddtion.Push(stringValueofINT(digitsummation)[0]);
                                carryover = null;
                            }

                        }
                        else
                        {
                            int digitsummation = ValueofDigitToAdd + ValueofDigitNumberActedOn;
                            if (digitsummation >= valueset.Length)
                            {
                                var cracktalstringvalueOFsummation = stringValueofINT(digitsummation);
                                if (cracktalstringvalueOFsummation.Length == 2) //and it better be
                                {
                                    resultofaddtion.Push(cracktalstringvalueOFsummation[1]);
                                    carryover = cracktalstringvalueOFsummation[0];
                                }
                            }
                            else
                            {
                                var cracktalstringvalueOFsummation = stringValueofINT(digitsummation);
                                resultofaddtion.Push(cracktalstringvalueOFsummation[0]);
                                carryover = null;
                            }
                        }

                    }
                }



            }
            if (carryover != null)
            {
                resultofaddtion.Push(carryover.Value);
                carryover = null;
            }

            while (resultofaddtion.Count > 0)
            {
                yield return resultofaddtion.Pop();
            }
            
            
           
            
        }

        public int GetMaxNumberOfThreads(ref int p)
        {
            var endvalue = new char[p];
            for (int i = 0; i < p; i++)
            {
                endvalue[i] = this.MaxUnitValue;
            }
            int result = -1;
            for(int x = 99;x>0;x--){
             var tempres = (RoughDivideBy(endvalue, x));
             if (checkGreaterOrEqualTo2(tempres))
                {
                    result = x;
                    break;
                }
            }

            return result;
            

        }

        private bool checkGreaterOrEqualTo2(char[] tempres)
        {
            var isvalid = false;
            var valueoftwo = stringValueofINT(2);
            if (valueoftwo.Length == tempres.Length)
            {
                 if (getIntValueofCractal(tempres) >= 2)
                    {
                        isvalid = true;
                    }

            }
            else if (tempres.Length > valueoftwo.Length)
            {
                isvalid = true;
            }

            return isvalid;
        }
    }


    public class IncrementEventArgs : EventArgs
    {
        public string Latestvalue { get; private set; }

        public IncrementEventArgs(char[] data)
        {
            
            Latestvalue = new String(data); 
        }



    }
}
